package com.fenxiangtech.mall.service.impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fenxiangtech.mall.exception.NoWeChatCodeException;
import com.fenxiangtech.mall.exception.UnknownErrorException;
import com.fenxiangtech.mall.exception.WeChatException;
import com.fenxiangtech.mall.exception.WeChatInnerException;
import com.fenxiangtech.mall.service.UserTokenService;
import com.fenxiangtech.mall.dao.UserDao;
import com.fenxiangtech.mall.dao.cache.RedisDao;
import com.fenxiangtech.mall.dto.SerializeWrapper;
import com.fenxiangtech.mall.entity.User;
import com.fenxiangtech.mall.entity.wx.WxApp;
import com.fenxiangtech.mall.enums.ScopeEnum;
import com.fenxiangtech.mall.util.HttpClientHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @author zhangjiashun
 */
@Service
public class UserTokenServiceImpl extends TokenServiceImpl implements UserTokenService {

    private static Logger log = LoggerFactory.getLogger(UserTokenServiceImpl.class);

    @Autowired
    private WxApp wxApp;

    @Autowired
    private UserDao userDao;

    @Autowired
    private RedisDao<SerializeWrapper<Map>> redisDao;

    private ObjectMapper mapper = new ObjectMapper();

    @Override
    public Map<String, String> getToken(String code) {
        Map<String, String> resultMap = new HashMap<>();
        if (code == null || code.equals("")) {
            throw new NoWeChatCodeException();
        }
        String wxResult = this.getWxResult(code);
        if (wxResult == null || "".equals(wxResult)) {
            throw new WeChatInnerException();
        }
        Map wxResultMap = null;
        try {
            wxResultMap = mapper.readValue(wxResult, Map.class);
        } catch (IOException e) {
            log.error("getToken(): " + e.toString());
            throw new UnknownErrorException();
        }
        boolean loginFail = wxResultMap.containsKey("errcode");
        if (loginFail) {
            int errcode = (int) wxResultMap.get("errcode");
            String errmsg = (String) wxResultMap.get("errmsg");
            throw new WeChatException(errcode, errmsg);
        } else {
            String token = this.grantToken(wxResultMap);
            resultMap.put("token", token);
        }
        return resultMap;
    }

    @Override
    public Long getCurrentUID() {
        Long uid = (Long) this.getCurrentTokenVar("uid");
        return uid;
    }

    @Override
    public Object getCurrentTokenVar(String key) {
        Map<String, String> tokenInfo = this.getTokenInfo();
        if (tokenInfo == null || !tokenInfo.containsKey(key)) {
            throw new RuntimeException("尝试获取的Token变量不存在");
        }
        return tokenInfo.get(key);
    }

    private String grantToken(Map wxResultMap) {
        String openid = (String) wxResultMap.get("openid");
        User user = userDao.getUserByOpenID(openid);
        if (user == null) {
            user = this.newUser(openid);
        }
        Long uid = user.getId();
        SerializeWrapper<Map> tokenValue = this.prepareCachedValue(wxResultMap, uid);
        String token = this.generateToken();
        return this.saveToCache(token, tokenValue);
    }

    /**
     * 准备缓存值, 由于Protostuff无法直接序列化Map,
     * 在这里使用SerializeWrapper<T>包装类
     * @param wxResultMap
     * @param uid
     * @return
     */
    private SerializeWrapper<Map> prepareCachedValue(Map wxResultMap, Long uid) {
        wxResultMap.put("uid", uid);
        wxResultMap.put("scope", ScopeEnum.USER.getCode());
        return new SerializeWrapper<>(wxResultMap);
    }

    /**
     * 创建新用户
     * @param openid
     */
    private User newUser(String openid) {
        User user = new User();
        user.setOpenid(openid);
        user.setCreateTime(new Date().getTime());
        int count = userDao.saveUser(user);
        if (count != 1) {
            throw new RuntimeException("创建用户失败");
        }
        return user;
    }

    /**
     * 向微信服务器发送http请求, 并返回微信服务器返回的结果
     * @param code 获取openID需要的code
     * @return
     */
    private String getWxResult(String code) {
        String wxAppID = wxApp.getWxAppID();
        String wxAppSecret = wxApp.getWxAppSecret();
        String wxLoginUrl = wxApp.getWxLoginUrl();
        wxLoginUrl = String.format(wxLoginUrl, wxAppID, wxAppSecret, code);
        String wxResult = HttpClientHelper.sendGet(wxLoginUrl, null, "utf-8");
        return wxResult;
    }
}
